package com.ikingtech.platform.service.system.variable.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ikingtech.framework.sdk.base.model.PageResult;
import com.ikingtech.framework.sdk.cache.constants.CacheConstants;
import com.ikingtech.framework.sdk.context.event.TenantDeleteEvent;
import com.ikingtech.framework.sdk.context.event.TenantInitEvent;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.system.variable.VariableEnum;
import com.ikingtech.framework.sdk.enums.system.variable.VariableTypeEnum;
import com.ikingtech.framework.sdk.log.embedded.annotation.OperationLog;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.variable.api.VariableApi;
import com.ikingtech.framework.sdk.variable.model.VariableDTO;
import com.ikingtech.framework.sdk.variable.model.VariableQueryParamDTO;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.system.variable.entity.VariableDO;
import com.ikingtech.platform.service.system.variable.exception.VariableExceptionInfo;
import com.ikingtech.platform.service.system.variable.service.VariableRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
@ApiController(value = "/system/variable", name = "系统管理-系统参数管理", description = "系统管理-系统参数管理")
public class VariableController implements VariableApi {

    private final VariableRepository repo;

    private final StringRedisTemplate redisTemplate;

    /**
     * 添加变量
     *
     * @param variable 变量DTO对象
     * @return 返回添加结果
     */
    @Override
    @OperationLog(value = "新增系统参数", dataId = "#_res.getData()")
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(VariableDTO variable) {
        // 将DTO对象转换为DO对象
        VariableDO entity = Tools.Bean.copy(variable, VariableDO.class);
        // 生成唯一ID
        entity.setId(Tools.Id.uuid());
        // 设置租户代码
        entity.setTenantCode(Me.tenantCode());
        // 保存实体到数据库
        this.repo.save(entity);
        // 将实体同步到缓存
        this.syncVariableToCache(entity);
        // 返回添加结果
        return R.ok(entity.getId());
    }

    /**
     * 删除操作
     *
     * @param id 要删除的数据的id
     * @return 删除结果
     */
    @Override
    @OperationLog(value = "删除系统参数")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        String variableKey = this.repo.getObj(Wrappers.<VariableDO>lambdaQuery().select(VariableDO::getVariableKey).eq(VariableDO::getId, id), String.class::cast);
        // 删除指定id的数据
        this.repo.removeById(id);
        this.syncVariableToCache(variableKey);
        return R.ok();
    }

    /**
     * 更新变量
     *
     * @param variable 变量DTO对象
     * @return 返回更新结果
     */
    @Override
    @OperationLog(value = "更新系统参数")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(VariableDTO variable) {
        // 根据变量ID获取原始实体
        VariableDO originEntity = this.repo.getById(variable.getId());
        if (originEntity == null) {
            throw new FrameworkException(VariableExceptionInfo.VARIABLE_NOT_FOUND);
        }
        // 如果变量类型为系统类型且变量值不合法，则抛出异常
        if (VariableTypeEnum.SYSTEM.name().equals(originEntity.getType()) &&
                !VariableEnum.validate(variable.getVariableKey(), variable.getVariableValue())) {
            throw new FrameworkException(VariableExceptionInfo.INVALID_VARIABLE_VALUE);
        }
        // 复制变量DTO对象到VariableDO对象，并设置租户代码
        VariableDO entity = Tools.Bean.copy(variable, VariableDO.class);
        entity.setTenantCode(Me.tenantCode());
        // 更新数据库中的变量
        this.repo.updateById(entity);
        // 同步变量到缓存
        this.syncVariableToCache(entity);
        return R.ok();
    }

    /**
     * 分页查询变量
     *
     * @param queryParam 查询参数
     * @return 分页结果
     */
    @Override
    public R<List<VariableDTO>> page(VariableQueryParamDTO queryParam) {
        return R.ok(PageResult.build(this.repo.page(new Page<>(queryParam.getPage(), queryParam.getRows()), VariableRepository.createWrapper(queryParam, Me.tenantCode()))).convert(entity -> {
            VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
            if (null != variable.getType()) {
                variable.setTypeName(variable.getType().description);
            }
            return variable;
        }));
    }

    /**
     * 获取所有变量
     *
     * @return 变量列表
     */
    @Override
    public R<List<VariableDTO>> all() {
        return R.ok(Tools.Coll.convertList(this.repo.list(Wrappers.<VariableDO>query().lambda().eq(VariableDO::getTenantCode, Me.tenantCode()).orderByDesc(VariableDO::getCreateTime)), entity -> {
            VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
            if (null != variable.getType()) {
                variable.setTypeName(variable.getType().description);
            }
            return variable;
        }));
    }

    /**
     * 获取变量详情
     * @param id 变量ID
     * @return 变量详情
     */
    @Override
    public R<VariableDTO> detail(String id) {
        // 通过ID获取变量实体
        VariableDO entity = this.repo.getById(id);
        if (null == entity) {
            throw new FrameworkException(VariableExceptionInfo.VARIABLE_NOT_FOUND);
        }
        // 将变量实体转换为变量DTO对象
        VariableDTO variable = Tools.Bean.copy(entity, VariableDTO.class);
        if (null != variable.getType()) {
            // 设置变量类型名称
            variable.setTypeName(variable.getType().description);
        }
        // 返回变量详情
        return R.ok(variable);
    }

    /**
     * 根据key获取value
     * @param key 键
     * @return 返回value
     */
    @Override
    public R<String> getValueByKey(String key) {
        return R.ok(this.repo.getObj(Wrappers.<VariableDO>lambdaQuery()
                .select(VariableDO::getVariableValue)
                .eq(VariableDO::getVariableKey, key)
                .eq(VariableDO::getTenantCode, Me.tenantCode()), String.class::cast));
    }

    /**
     * 处理租户初始化事件
     * @param event 租户初始化事件对象
     */
    @EventListener
    public void tenantInitEventListener(TenantInitEvent event) {
        // 保存变量到数据库
        this.repo.saveBatch(Tools.Array.convertList(VariableEnum.values(), variable -> {
            VariableDO variableDO = new VariableDO();
            variableDO.setId(Tools.Id.uuid());
            variableDO.setTenantCode(event.getCode());
            variableDO.setVariableKey(variable.name());
            variableDO.setVariableValue(variable.value);
            variableDO.setType(variable.type.name());
            variableDO.setName(variable.description);
            return variableDO;
        }));
    }

    /**
     * 处理租户删除事件
     * @param event 租户删除事件对象
     */
    @EventListener
    public void tenantDeleteEventListener(TenantDeleteEvent event) {
        // 根据租户代码查询变量DO对象，并删除
        this.repo.remove(Wrappers.<VariableDO>lambdaQuery().eq(VariableDO::getTenantCode, event.getCode()));
        // 删除缓存中的系统变量
        this.redisTemplate.delete(CacheConstants.systemVariableFormat(Me.tenantCode()));
    }

    /**
     * 同步变量缓存
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(fixedDelay = 5 * 60, initialDelay = 60, timeUnit = TimeUnit.SECONDS)
    public void syncVariableCache() {
        // 获取所有变量实体
        List<VariableDO> entities = this.repo.list();
        // 同步变量到缓存
        this.syncVariableToCache(entities);
    }

    /**
     * 将变量同步到缓存中
     * @param variableKey 变量的键名
     */
    private void syncVariableToCache(String variableKey) {
        // 从Redis缓存中删除指定键名的变量
        this.redisTemplate.opsForHash().delete(CacheConstants.systemVariableFormat(Me.tenantCode()), variableKey);
    }

    /**
     * 将变量同步到缓存中
     * @param entity 变量实体
     */
    private void syncVariableToCache(VariableDO entity) {
        if (null == entity) {
            return;
        }
        // 将变量同步到缓存中
        this.redisTemplate.opsForHash().put(CacheConstants.systemVariableFormat(entity.getTenantCode()), entity.getVariableKey(), entity.getVariableValue());
    }

    /**
     * 将变量实体列表同步到缓存
     *
     * @param entities 变量实体列表
     */
    private void syncVariableToCache(List<VariableDO> entities) {
        if (Tools.Coll.isBlank(entities)) {
            return;
        }
        // 根据租户代码分组同步到Redis缓存中
        Tools.Coll.convertGroup(entities, VariableDO::getTenantCode).forEach((tenantCode, groupedEntities) -> this.redisTemplate.opsForHash().putAll(CacheConstants.systemVariableFormat(tenantCode), Tools.Coll.convertMap(groupedEntities, VariableDO::getVariableKey, VariableDO::getVariableValue)));
    }
}
